package com.lzk.jetpacktest.matrix

import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Matrix
import android.graphics.Paint
import android.graphics.Point
import android.graphics.PointF
import android.graphics.RectF
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import android.view.View
import android.view.View.OnTouchListener
import kotlin.math.sqrt

class MatrixImageView : View,OnTouchListener {

    private var mBitmap: Bitmap? = null
    private val mMatrix: Matrix = Matrix()
    private var mInitScale: Float = 1f
    private var mCurScale: Float = 1f
    private var mLastScale: Float = 1f

    private val mDownPoint1 = PointF()
    private val mDownPoint2 = PointF()
    private val mLastMidPointF = PointF()
    private var mTranslateX = 0f
    private var mTranslateY = 0f
    private val TOUCH_SLOP = 1

    private lateinit var mPaint: Paint

    constructor(context: Context?) : this(context,null)
    constructor(context: Context?,attributes: AttributeSet?) : this(context,attributes,0)
    constructor(context: Context?, attributeSet: AttributeSet?, defStyleAttr: Int) : super(context, attributeSet, defStyleAttr){
        init()
    }

    private fun init(){
        setLayerType(LAYER_TYPE_SOFTWARE,null)
        setOnTouchListener(this)
        mPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
            style = Paint.Style.STROKE
            color = Color.RED
            strokeWidth = 10f
        }
    }

    private fun initMatrix(){
        if (mBitmap != null && width > 0 && height > 0){
            val dw = mBitmap!!.width
            val dh = mBitmap!!.height

            var scale = 1f

            if (dw > width && dh < height){
                scale = width*1f/dw*1f
            }
            if (dw < width && dh > height){
                scale = height*1f/dh*1f
            }
            if (dw <= width && dh <= height || dw >= width && dh >=height){
                scale = (width*1f / dw*1f).coerceAtMost(height*1f / dh*1f)
            }
            val x = width*0.5f - dw*scale*0.5f
            val y = height*0.5f - dh*scale*0.5f

            mCurScale = scale
            mInitScale = scale
            mLastScale = scale
            mTranslateX = x
            mTranslateY = y
            mMatrix.reset()
            mMatrix.postScale(scale,scale)
            mMatrix.postTranslate(x,y)
        }
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        initMatrix()
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        mBitmap?.let { target ->
            canvas.save()
            canvas.setMatrix(mMatrix)
            canvas.drawBitmap(target,0f,0f,null)
            canvas.restore()
        }
    }

    override fun onTouch(v: View?, event: MotionEvent): Boolean {
        if (event.pointerCount > 2){
            return true
        }


        when(event.actionMasked){
            MotionEvent.ACTION_DOWN -> {
                mDownPoint1.set(event.x,event.y)
            }
            MotionEvent.ACTION_POINTER_DOWN -> {
                mDownPoint2.set(event.getX(1),event.getY(1))
                val point = getMidpoint(event.getX(0),event.getY(0),event.getX(1),event.getY(1))
                mLastMidPointF.set(point)
            }
            MotionEvent.ACTION_MOVE -> {
                if (event.pointerCount == 2){
                    val curMidPoint = getMidpoint(event.getX(0),event.getY(0),event.getX(1),event.getY(1))
                    val moveDistance = getDistance(curMidPoint.x,curMidPoint.y,mLastMidPointF.x,mLastMidPointF.y)
                    if (moveDistance > TOUCH_SLOP){
                        val curDistance = getDistance(event.getX(0),event.getY(0),event.getX(1),event.getY(1))
                        val lastDistance = getDistance(mDownPoint1.x,mDownPoint1.y,mDownPoint2.x,mDownPoint2.y)
                        val scale = curDistance/lastDistance
                        mCurScale = scale*mLastScale
                        val dx = curMidPoint.x - mLastMidPointF.x
                        val dy = curMidPoint.y - mLastMidPointF.y
                        mTranslateX += dx
                        mTranslateY += dy
                        mMatrix.reset()
                        mMatrix.postScale(mCurScale,mCurScale)
                        mMatrix.postTranslate(mTranslateX,mTranslateY)
                        invalidate()
                    }
                    mLastMidPointF.set(curMidPoint)
                }
            }
            MotionEvent.ACTION_POINTER_UP,
            MotionEvent.ACTION_UP -> {
                mLastScale = mCurScale
            }
        }
        return true
    }

    private fun getMidpoint(x1: Float,y1: Float,x2: Float,y2: Float): PointF{
        val dx = (x1+x2)/2f
        val dy = (y1+y2)/2f
        return PointF(dx,dy)
    }

    private fun getDistance(x1: Float,y1: Float,x2: Float,y2: Float): Float{
        val dx = x1 - x2
        val dy = y1 - y2
        return sqrt(dx*dx+dy*dy)
    }

    fun setBitmap(bitmap: Bitmap){
        mBitmap = bitmap
        mMatrix.reset()
        initMatrix()
        invalidate()
    }
}